import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Hashtable;
import java.util.LinkedList;


public class SymbolTable {

	/**
	 * @author	Rony Lahav	ID 043489889
	 * @author	Ori Weisler	ID 060512878
	 * @date	26/05/2009
	 */
	private static int memLoc = 500;	//next location in memory
	private static int symLoc = 0;		//next location in symbol table
	private static Hashtable<String, Table> symbolTableByName = new Hashtable<String, Table>();
	private static Hashtable<Integer, Table> symbolTableByLoc = new Hashtable<Integer, Table>(); 
	private static int[] memory = new int[1024];
	
	private static BufferedReader prog;	//for the JVM program content
	private static final String PROG_FILE_NAME = "input.txt";
	
	private static class Table{
		public int curLocation;	//current location in symbol table
		public String varName;	//i
		public String varType;	//var/const/type
		public int varData=-1;		//int / pointer to next symbol table
		public int varMem=-1;		//pointer to memory / const value
		public int nextSymTable=-1;//pointer to next symbol table
		
		public void print(){
			System.out.println("*****symbol at location " + curLocation + "*****");
			if (varName.equals("NIL")){
				System.out.println("NIL");
				return;
			}
			if (!varName.startsWith("#"))		//name starts with # for array and struct
				System.out.println(varName);
			System.out.println(varType);		//type always full
			if (varData<0){						//for struct
				System.out.println(nextSymTable);
				return;
			}
			if (varData == 1002)				//1002=int else address of array/type 
				System.out.println("int");
			else
				System.out.println(varData);
			if (varMem>=0)						//if -1 then const or type, else memory
				System.out.println(varMem);
			else
				System.out.println("-");
			if (!varName.startsWith("#")){					//for array name starts with # (so nothing to print), else next symbol or null
				if (nextSymTable>=0)	//<0 for type int chain
					System.out.println(nextSymTable);
				else
					System.out.println("NIL");
			}
			
		}
	}
	
	private static LinkedList<Table> createIntSymTable(boolean isConst, String line){
		//for "int i=1,j;"
		LinkedList<Table> tableList = new LinkedList<Table>();
		Table table;
		String[] splittedVars = line.split(",");
		for (String var : splittedVars) {
			table = new Table();
			table.curLocation = symLoc;
			symLoc+=20;
			table.nextSymTable=symLoc;
			table.varType = "var";
			table.varData = 1002;
			if (!isConst){
				table.varMem = memLoc;
				memLoc+=4;
			}
			else
				table.varType="const";
			if (var.contains("=")){
				String[] values = var.split("=");
				table.varName = values[0].trim();
				if (isConst)
					table.varMem=Integer.parseInt(values[1].trim());
				else
					memory[table.varMem-500]=Integer.parseInt(values[1].trim());
			}
			else
				table.varName = var.trim();
			table.print();
			tableList.add(table);
		}
		return tableList;
	}
	
	private static LinkedList<Table> createIntArraySymTable(boolean isConst, String line){
		//for "int a[10],b[5];"
		LinkedList<Table> tableList = new LinkedList<Table>();
		Table table;
		String[] splittedVars = line.split(",");
		for (String var : splittedVars) {
			table = new Table();
			table.curLocation = symLoc;
			symLoc+=20;
			String[] values = var.split("\\[");
			table.varName = values[0].trim();
			table.varType = "var";
			table.varData = symLoc;
			int length = Integer.parseInt(values[1].trim().substring(0, values[1].length()-1));
			//points to an array symbol
			Table arrayTable = createArraySymbolTable(table.varName, length);
			if (!isConst){
				table.varMem = memLoc;
				memLoc+=4*length;
			}
			else
				table.varType="const";
			table.nextSymTable=symLoc;	//was increased inside "createArraySymbolTable"
			table.print();
			tableList.add(table);
			arrayTable.print();
			tableList.add(arrayTable);
		}
		return tableList;
	}
	
	private static Table createArraySymbolTable(String varName, int length){
		//for array symbol of "a[10]"
		Table table = new Table();
		table.curLocation = symLoc;
		symLoc+=12;
		table.varName = "#" + varName;
		table.varType = "array";
		table.varData = 1002;
		table.varMem = length;		
		return table;
	}
	
	private static LinkedList<Table> createTypeSymTable(boolean isConst, String line){
		//for "z {int x;int y;} s,t" (for example)
		LinkedList<Table> tableList = new LinkedList<Table>();
		String[] data = line.split("\\{");
		String typeName = data[0].trim();
		data = data[1].split("\\}");
		String ints = data[0].trim();
		String[] vars=data[1].trim().split(",");
		Table table = new Table();
		table.curLocation = symLoc;
		symLoc+=20;
		table.varName=typeName;
		table.varType="type";
		table.varData=symLoc;
		//points to a struct symbol
		Table structTable = createTypeStructSymTable(typeName);
		//that points to a chain of ints
		LinkedList<Table> intChain = createTypeIntChain(ints);
		table.nextSymTable=symLoc;	//was increased inside "createTypeStructSymTable" and "createTypeIntChain"
		table.print();
		tableList.add(table);
		structTable.print();
		tableList.add(structTable);
		int jumpCount=0;
		for (Table intTable : intChain) {
			intTable.print();
			tableList.add(intTable);
			++jumpCount;
		}
		for (String var : vars) {
			Table varTable = creatTypeVarSymTable(isConst, var.trim(), table.varData, jumpCount);
			varTable.print();
			tableList.add(varTable);
		}
		return tableList;
	}
	
	private static Table createTypeStructSymTable(String typeName){
		//a struct symbol
		Table table = new Table();
		table.curLocation=symLoc;
		symLoc+=8;
		table.varName="#" + typeName;
		table.varType="struct";
		table.nextSymTable=symLoc;
		return table;
	}
	
	private static LinkedList<Table> createTypeIntChain(String ints){
		//creates a chain of int fields for a struct
		LinkedList<Table> tableList = new LinkedList<Table>();
		Table table;
		String[] splittedVars = ints.split(";");
		for (int i=0;i<splittedVars.length;++i) {
			table = new Table();
			table.curLocation = symLoc;
			symLoc+=20;
			table.varName = splittedVars[i].trim().split(" ")[1];
			table.varType="field";
			table.varData=1002;
			if (i+1 < splittedVars.length)	//there's another int in the chain
				table.nextSymTable=symLoc;
			tableList.add(table);
		}		
		return tableList;
	}
	
	private static Table creatTypeVarSymTable(boolean isConst, String varName, int varData, int jumpCount){
		//creates symbols for vars of type "type" 
		Table table = new Table();
		table.curLocation = symLoc;
		symLoc+=20;
		table.varName=varName;
		table.varType="var";
		table.varData=varData;
		if (!isConst){
			table.varMem=memLoc;
			memLoc+=4*jumpCount;
		}
		else
			table.varType="const";
		table.nextSymTable=symLoc;
		return table;
	}
	
	
	public static void main(String[] args) throws IOException {
		try {
			prog = new BufferedReader(new FileReader(PROG_FILE_NAME));
			String[] splittedLine;
			String line;
			line = prog.readLine();	//read first JVM command
			do{
				if (line.startsWith("i")){	//int
					if (line.contains("[")){	//int array
						LinkedList<Table> tableList = createIntArraySymTable(false, line.substring(4,line.length()-1));	//without "int " at beginning and ";" at end
						for (Table table : tableList) {
							symbolTableByName.put(table.varName, table);
							symbolTableByLoc.put(table.curLocation, table);
						}
					}
					else{	//standard int
						LinkedList<Table> tableList = createIntSymTable(false, line.substring(4,line.length()-1));	//without "int " at beginning and ";" at end
						for (Table table : tableList) {
							symbolTableByName.put(table.varName, table);
							symbolTableByLoc.put(table.curLocation, table);
						}
					}
				}
				else if (line.startsWith("s")){	//struct
					LinkedList<Table> tableList = createTypeSymTable(false, line.substring(7,line.length()-1));	//without "struct " at beginning and ";" at end
					for (Table table : tableList) {
						symbolTableByName.put(table.varName, table);
						symbolTableByLoc.put(table.curLocation, table);
					}
				}
				else{	//const
					line = line.substring(6);	//without "const " at beginning
					if (line.startsWith("i")){	//int
						if (line.contains("[")){	//int array
							LinkedList<Table> tableList = createIntArraySymTable(true, line.substring(4,line.length()-1));	//without "int " at beginning and ";" at end
							for (Table table : tableList) {
								symbolTableByName.put(table.varName, table);
								symbolTableByLoc.put(table.curLocation, table);
							}
						}
						else{	//standard int
							LinkedList<Table> tableList = createIntSymTable(true, line.substring(4,line.length()-1));	//without "int " at beginning and ";" at end
							for (Table table : tableList) {
								symbolTableByName.put(table.varName, table);
								symbolTableByLoc.put(table.curLocation, table);
							}
						}
					}
					else if (line.startsWith("s")){	//struct
						LinkedList<Table> tableList = createTypeSymTable(true, line.substring(7,line.length()-1));	//without "struct " at beginning and ";" at end
						for (Table table : tableList) {
							symbolTableByName.put(table.varName, table);
							symbolTableByLoc.put(table.curLocation, table);
						}
					}
				}
				
				line = prog.readLine();	//read next command
			}while(line!=null);	//error or EOF
			//create NULL reference for last symbol table
			Table nullTable = new Table();
			nullTable.curLocation=symLoc;
			nullTable.varName="NIL";
			nullTable.print();
			symbolTableByName.put(nullTable.varName, nullTable);
			symbolTableByLoc.put(nullTable.curLocation, nullTable);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally{
			prog.close();
		}
	}

}
